package consumer

import (
	"base/common/log"
	"base/common/queue"
	"base/consumer/eat/conAbstract"
	"base/consumer/eat/conFactory"
	"context"
	"time"
)

// 消费队列任务
func Consume(queueName string, tries int, outTime int) {

	//获取 对应结构体
	obj := getStruct(queueName)

	//如果 队列类型不存在 则抛出错误
	if obj == nil {
		panic("队列消费者不存在")
	}

	//声明 全局管道
	ch := make(chan string, 50)

	// 启动 队列读取
	queue.Consumer(queueName, &ch)

	out := time.Second * time.Duration(outTime)

	//循环处理管道内的任务
	for data := range ch {
		//将json 转为map
		m, e := conAbstract.GetMapData(data)

		// 转化数据失败
		if e != nil {
			continue
		}

		//初始化 用于为参数记错
		i := 0

	Loop:
		c, _ := context.WithTimeout(context.Background(), out) //设置上下文超时时间
		ctx, cancel := context.WithCancelCause(c)
		i++
		//上下文超时时间 与 预期超时时间
		//1、上下文超时时间是指的 这个上下文 从生成到结束的全局 这个上下文的整个生命周期
		//2、预期超时 因为上下文是可能贯穿全局 你不直到 执行到你这里还剩余多少时间 可能很长 也可能很短
		//   这时你需要给与一个预期超时时间，举例全局上下文还有 60秒 而你希望你的程序执行不超过30秒 这时预期超时
		//	 就可以发挥作用

		//执行消费者
		go func() {
			err := obj.Handle(m)
			obj.Error(err)
			cancel(err)
		}()

		select {
		case <-ctx.Done():
			//上下文超时
			if context.Cause(ctx) == context.DeadlineExceeded {
				log.ELog("上下文超时", map[string]any{
					"Data": data,
				})
				continue
			}

			//如果不等于自己关闭 并且 字符串长度 > 1
			if context.Cause(ctx) != context.Canceled && len(context.Cause(ctx).Error()) > 1 {
				//最大允许错误次数 如果设置了且不等于0 重新执行方法
				if tries > i {
					goto Loop //跳到代码段再次执行
				} else {
					log.ELog("执行多次导致任务失败", map[string]any{
						"Data": data,
						"err":  context.Cause(ctx).Error(),
					})
					continue //以达到最大次数 跳出当前次循环
				}
			}
			//预期超时时间
		case <-time.After(out):
			log.ELog("任务超时", map[string]any{
				"Data": data,
			})
			continue
		}

		//消费间歇时间 方式开启过多协程
		time.Sleep(time.Second * time.Duration(conAbstract.Sleeptime))

	}

}

// 获取 消费者
func getStruct(queueName string) conAbstract.Consumer {

	switch queueName {
	case "demo":
		return &conFactory.Demo{}
	}
	return nil
}
